﻿using ICSharpCode.SharpZipLib.GZip;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;

namespace NetWorkMsg
{
    // 关于消息的常量声明
    public static class MessageConst
    {
        public const int StreamBufferSize = 1 << 16;
        public const int MessageHeadSize = 2;
    }

    // 通信协议中可打包数据的接口
    public interface IPackable
    {
        void Pack(MemoryStream stream);
    }

    public interface IIdPackable : IPackable
    {
        uint PackableId { get; }
    }

    public interface IServerCallbackable : IIdPackable
    {
        uint UnPackableId { get; }
    }

    public interface IUnPackable
    {
        void UnPack(MemoryStream stream);
    }

    public interface IIdUnPackable : IUnPackable
    {
        uint UnPackableId { get; }
    }

    /* 有些数据需要一个大的Buffer来做各种数据转换,但是实际传输的数据量
     * 往往小于Buffer的上限,所以这里加了这么一个结构来方便传输数据,目前
     * 实现简单,且只有上行才会用到
    */
    public class NetMsgBuffer
    {
        public byte[] Data;
        public int Offset;
        public int Used;

        public void Clear()
        {
            Offset = 0;
            Used = 0;
        }
    }

    public static class PackAuxiliary
    {
        public static void Write(MemoryStream stream, bool value)
        {
            Write(stream, value ? (uint)1 : 0);
        }

        public static void Write(MemoryStream stream, uint value)
        {
            while (value >= 0x80)
            {
                var val = (byte)(value | 0x80);
                stream.WriteByte(val);
                value >>= 7;
            }

            stream.WriteByte((byte)value);
        }

        public static void Write(MemoryStream stream, List<uint> value)
        {
            var len = (uint)value.Count;
            Write(stream, len);
            for (var i = 0; i < len; ++i)
                Write(stream, value[i]);
        }

        public static void Write(MemoryStream stream, BitVector32 value)
        {
            var data = (uint)value.Data;
            Write(stream, data);
        }

        public static void Write(MemoryStream stream, BitArray value)
        {
            var len = (int)Math.Ceiling(value.Count / 32f);
            var arr = new int[len];
            value.CopyTo(arr, 0);
            WriteIsAsUs(stream, arr);
        }

        private static uint ZigzagEncode32(int value)
        {
            return (uint)(((uint)value << 1) ^ (value >> 31));
        }

        private static void Write(MemoryStream stream, int value)
        {
            Write(stream, ZigzagEncode32(value));
        }

        public static void Write(MemoryStream stream, List<int> value)
        {
            var len = (uint)value.Count;
            Write(stream, len);
            for (var i = 0; i < len; ++i)
                Write(stream, value[i]);
        }

        public static void Write(MemoryStream stream, ulong value)
        {
            var high = (uint)(value >> 32);
            var low = (uint)value;

            if (high == 0)
            {
                Write(stream, low);
                return;
            }

            // 没必要用循环和乘法(虽然相信编译器和JIT的能力)
            var l1 = (byte)(low | 0x80);
            var l2 = (byte)(low >> 7 | 0x80);
            var l3 = (byte)(low >> 14 | 0x80);
            var l4 = (byte)(low >> 21 | 0x80);
            stream.WriteByte(l1);
            stream.WriteByte(l2);
            stream.WriteByte(l3);
            stream.WriteByte(l4);

            if (high < 8)
            {
                var val = (byte)((high << 4) | (low >> 28));
                stream.WriteByte(val);
                return;
            }

            var l5 = (byte)(((high & 7) << 4) | (low >> 28) | 0x80);
            stream.WriteByte(l5);

            high >>= 3;
            Write(stream, high);
        }

        public static void Write(MemoryStream stream, List<ulong> value)
        {
            var len = (uint)value.Count;
            Write(stream, len);
            for (var i = 0; i < len; ++i)
                Write(stream, value[i]);
        }

        private static ulong ZigzagEncode64(long value)
        {
            return ((ulong)value << 1) ^ (ulong)(value >> 63);
        }

        private static void Write(MemoryStream stream, long value)
        {
            Write(stream, ZigzagEncode64(value));
        }

        public static void Write(MemoryStream stream, List<long> value)
        {
            var len = (uint)value.Count;
            Write(stream, len);
            for (var i = 0; i < len; ++i)
                Write(stream, value[i]);
        }

        public static void Write(MemoryStream stream, float value)
        {
            var bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
            {
                for (var i = 3; i >= 0; --i)
                    stream.WriteByte(bytes[i]);
            }
            else stream.Write(bytes, 0, 4);
        }

        public static void Write(MemoryStream stream, string value)
        {
            var bytes = Encoding.UTF8.GetBytes(value);
            // 不校验bytes长度(能达到2^32长度,一定是出问题了)
            Write(stream, bytes);
        }

        public static void Write(MemoryStream stream, List<string> value)
        {
            var len = (uint)value.Count;
            Write(stream, len);
            for (var i = 0; i < len; ++i)
                Write(stream, value[i]);
        }

        public static void Write(MemoryStream stream, byte[] value)
        {
            // 不校验value长度
            Write(stream, (uint)value.Length);
            stream.Write(value, 0, value.Length);
        }

        public static void Write(MemoryStream stream, NetMsgBuffer value)
        {
            Write(stream, (uint)value.Used);
            stream.Write(value.Data, value.Offset, value.Used);
        }

        public static void Write(MemoryStream stream, IPackable value)
        {
            value.Pack(stream);
        }

        // 压缩写
        public static void WriteCompressible(MemoryStream stream, string value)
        {
            var bytes = Encoding.UTF8.GetBytes(value);
            WriteCompressible(stream, bytes);
        }

        private static void WriteCompressible(MemoryStream stream, byte[] bytes)
        {
            using (var compressStream = new MemoryStream())
            {
                using (var gzip = new GZipOutputStream(compressStream))
                {
                    // GZipOutputStream需要手动调用Finish或者Close才会执行压缩操作
                    gzip.Write(bytes, 0, bytes.Length);
                }

                var compressedData = compressStream.ToArray();

                // 压缩后的格式: 是否压缩标志位 长度 字节
                if (compressedData.Length + 1 < bytes.Length)
                {
                    Write(stream, true);
                    Write(stream, compressedData);
                }
                else
                {
                    // 压缩后变大了,则不压缩
                    Write(stream, false);
                    Write(stream, bytes);
                }
            }
        }

        public static void WriteIsAsUs(MemoryStream stream, IList<int> value)
        {
            var len = (uint)value.Count;
            Write(stream, len);
            for (var i = 0; i < len; ++i)
            {
                var uval = (uint)value[i];
                Write(stream, uval);
            }
        }
    }

    public static class UnPackAuxiliary
    {
        public static void Read(MemoryStream stream, out bool value)
        {
            Read(stream, out uint res);
            value = res == 1;
        }

        public static void Read(MemoryStream stream, out uint value)
        {
            value = 0;
            var shift = 0;
            var val = 0;

            do
            {
                val = stream.ReadByte();
                value |= (uint)((val & 0x7F) << shift);
                shift += 7;
            }
            while (0 != (val & 0x80));
        }

        public static void Read(MemoryStream stream, out List<uint> value)
        {
            Read(stream, out uint len);
            value = new List<uint>((int)len);
            for (var i = 0; i < len; ++i)
            {
                Read(stream, out uint tmp);
                value.Add(tmp);
            }
        }

        public static void Read(MemoryStream stream, out BitVector32 value)
        {
            Read(stream, out uint data);
            value = new BitVector32((int)data);
        }

        public static void Read(MemoryStream stream, out BitArray value)
        {
            ReadUsAsIs(stream, out List<int> list);
            value = new BitArray(list.ToArray());
        }

        private static int ZigzagDecode32(uint value)
        {
            return (int)((value >> 1) ^ -(value & 1));
        }

        public static void Read(MemoryStream stream, out int value)
        {
            Read(stream, out uint val);
            value = ZigzagDecode32(val);
        }

        public static void Read(MemoryStream stream, out List<int> value)
        {
            Read(stream, out uint len);
            value = new List<int>((int)len);
            for (var i = 0; i < len; ++i)
            {
                Read(stream, out int tmp);
                value.Add(tmp);
            }
        }

        public static void Read(MemoryStream stream, out ulong value)
        {
            value = 0;
            var shift = 0;
            ulong val = 0;

            do
            {
                val = (ulong)stream.ReadByte();
                value |= (ulong)((val & 0x7F) << shift);
                shift += 7;
            }
            while (0 != (val & 0x80));
        }

        public static void Read(MemoryStream stream, out List<ulong> value)
        {
            Read(stream, out uint len);
            value = new List<ulong>((int)len);
            for (var i = 0; i < len; ++i)
            {
                Read(stream, out ulong tmp);
                value.Add(tmp);
            }
        }

        private static long ZigzagDecode64(ulong value)
        {
            return (long)(value >> 1) ^ -(long)(value & 1);
        }

        private static void Read(MemoryStream stream, out long value)
        {
            Read(stream, out ulong val);
            value = ZigzagDecode64(val);
        }

        public static void Read(MemoryStream stream, out List<long> value)
        {
            Read(stream, out uint len);
            value = new List<long>((int)len);
            for (var i = 0; i < len; ++i)
            {
                Read(stream, out long tmp);
                value.Add(tmp);
            }
        }

        public static void Read(MemoryStream stream, out float value)
        {
            var bytes = new byte[4];
            if (BitConverter.IsLittleEndian)
            {
                for (var i = 3; i >= 0; --i)
                    bytes[i] = (byte)stream.ReadByte();
            }
            else stream.Read(bytes, 0, 4);

            value = BitConverter.ToSingle(bytes, 0);
        }

        public static void Read(MemoryStream stream, out string value)
        {
            Read(stream, out byte[] bytes);
            value = Encoding.UTF8.GetString(bytes);
        }

        public static void Read(MemoryStream stream, out List<string> value)
        {
            Read(stream, out uint len);
            value = new List<string>((int)len);
            for (var i = 0; i < len; ++i)
            {
                Read(stream, out string tmp);
                value.Add(tmp);
            }
        }

        public static void Read(MemoryStream stream, out byte[] value)
        {
            Read(stream, out uint len);
            var bytes = new byte[len];
            stream.Read(bytes, 0, (int)len);
            value = bytes;
        }

        // 不存在包含Buffer类型的下发数据,但因为现在上行/下行是配套的,所以这里要加上这个
        public static void Read(MemoryStream stream, out NetMsgBuffer value)
        {
            Read(stream, out byte[] bytes);
            value = new NetMsgBuffer
            { Data = bytes, Used = bytes.Length };
        }

        // 解压缩读
        public static void ReadCompressible(MemoryStream stream, out string value)
        {
            ReadCompressible(stream, out byte[] bytes);
            value = Encoding.UTF8.GetString(bytes);
        }

        public static void ReadCompressible(MemoryStream stream, out byte[] value)
        {
            Read(stream, out bool isCompressed);
            Read(stream, out byte[] bytes);
            if (isCompressed)
            {
                using (MemoryStream unCompressStream = new MemoryStream(bytes), normalStream = new MemoryStream()
                )
                {
                    using (var gzip = new GZipInputStream(unCompressStream))
                    {
                        gzip.CopyTo(normalStream);
                        value = normalStream.ToArray();
                    }
                }
            }
            else value = bytes;
        }

        public static void ReadUsAsIs(MemoryStream stream, out List<int> value)
        {
            Read(stream, out uint len);
            value = new List<int>((int)len);
            for (var i = 0; i < len; ++i)
            {
                Read(stream, out uint tmp);
                value.Add((int)tmp);
            }
        }

        public static int ReadHead(MemoryStream stream)
        {
            var len = (int)stream.ReadByte();
            return len << 8 | stream.ReadByte();
        }

        public static uint ReadId(MemoryStream stream)
        {
            Read(stream, out uint id);
            return id;
        }
    }
}